Skip to content

Build GHC with cabal-install and a Makefile #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 28 commits into from

Conversation

hsyl20
Copy link

@hsyl20 hsyl20 commented Nov 27, 2024

Note that we need a patched cabal: https://github.com/hsyl20/cabal/tree/hsyl20/per-file-extra-source-options

  • Build ghc-stage1
  • Generate valid non-cross stage1 settings (reusing stage0's settings for the most part)
  • Build boot libraries with stage1: ghc-prim, ghc-internal, base
  • Build ghc-stage2
  • Build a bindist
  • For every host/arch we want to build a compiler for:
    • generate valid stage2 settings for the target (using ghc-toolchain?)
    • use ghc-stage2 with these settings to build root libraries
    • use ghc-stage2 with these settings to build iserv
    • (optional) use ghc-stage2 with these settings to cross-build a GHC
  • run the testsuite on CI

@hsyl20
Copy link
Author

hsyl20 commented Nov 27, 2024

Current status: it builds some ghc program in _build/stage0/bin/ghc. It seems to be linked with the wrong ghc-boot because it reports a GHC version of 9.8.2 (my bootstrap GHC).

@hsyl20
Copy link
Author

hsyl20 commented Nov 27, 2024

Ah, I need to pass some environment variable to ghc-boot's Setup.hs

Copy link

@angerman angerman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @hsyl20 this looks like a great start!

@hsyl20
Copy link
Author

hsyl20 commented Nov 28, 2024

Now it seems like cabal doesn't like empty package databases:

Error: [Cabal-9076]
failed to parse output of 'ghc-pkg dump'

Edit: that was my mistake (a debug statement in ghc-pkg...). Now it works.

@andreabedini
Copy link
Member

Effectively we want to completely disable the solver. We can add
active-respositories: :none to cabal.project and remove the source constraints.

@hsyl20
Copy link
Author

hsyl20 commented Nov 29, 2024

Effectively we want to completely disable the solver. We can add active-respositories: :none to cabal.project and remove the source constraints.

I've disabled it for stage1 where we start fresh to build boot libraries.

@angerman angerman changed the base branch from master to tmp/make-build-offset January 27, 2025 23:44
@hsyl20
Copy link
Author

hsyl20 commented Jan 31, 2025

With the latest commit, we build settings with ghc-toolchain. So now we should be able to generate settings for a different toolchain, hence be close to support cross-compilation. It probably won't work for now because we only support using the system libffi.

@angerman
Copy link

angerman commented Feb 1, 2025

it, we build settings with ghc-toolchain. So now we should be able to generate settings for a different toolchain, hence be close to support cross-compilatio

Why not? Cross toolchains can come with libffi as well?

@Ericson2314
Copy link

Any particular reason you choose shell out to ghc-toolchain instead of using it as a library? I guess future settings-file-aware cabal-install would also shell out.

@hsyl20
Copy link
Author

hsyl20 commented Feb 3, 2025

Any particular reason you choose shell out to ghc-toolchain instead of using it as a library? I guess future settings-file-aware cabal-install would also shell out.

Well initially the script was supposed to be written only as a Makefile, not as a Haskell script. I've switched to Haskell for my own sanity but in the future we might switch back to a Makefile, hence we'll need to shell out to other programs like ghc-toolchain.

@hsyl20
Copy link
Author

hsyl20 commented Feb 3, 2025

Why not? Cross toolchains can come with libffi as well?

If cross toolchains come with libffi then it should work indeed. But is libffi part of cross toolchains? I think they only provide libc, libm, libpthread, etc. In my case (with ArchLinux), I'd have to do some headstands to compile libffi manually with the cross-compiler and put it in the library path when building the rts.

So maybe we need something (nix?) to provide the cross-toolchain and the basic dependencies (libffi, libgmp, etc.)?

@angerman
Copy link

angerman commented Feb 4, 2025

If cross toolchains come with libffi then it should work indeed. But is libffi part of cross toolchains? I think they only provide libc, libm, libpthread, etc. In my case (with ArchLinux), I'd have to do some headstands to compile libffi manually with the cross-compiler and put it in the library path when building the rts.

You'll need to do this anyway for any system library. Compiling system libraries with the cross target toolchain is something we can expect the end user to do. I wrote about building libffi for raspberry-pi 8 years ago. You should be able to build any library for your target using your cross-compilation toolchain. Your cross compilation toolchain will usually be:

  • bunch of compilers (cc, cxx, as)
  • build tools: (ld, ar, ...)
  • sys root: for your header files.
  • libc, and some other libraries your compiler comes with (libm, libatomic, libstdcxx, ...)

Based on that you can basically build any library you need. And often times you will need to as, you will need openssl, or similar.

The question then is mostly just how do you setup your environment for your compiler to find stuff automatically. CFLAGS/LDFLAGS/...

@hsyl20 hsyl20 mentioned this pull request Feb 4, 2025
@hsyl20
Copy link
Author

hsyl20 commented Feb 5, 2025

Making progress:

> TEST_HC=/home/hsyl20/projects/ghc/stable/_build/stage2/bin/ghc make test
...
SUMMARY for test run started at Wed Feb  5 10:35:46 2025
0:28:57.534353 spent to go through
   10414 total tests, which gave rise to
   31956 test cases, of which
   21482 were skipped
       8 had missing libraries

    9529 expected passes
     118 expected failures

      51 caused framework failures
       0 caused framework warnings
       2 unexpected passes
     295 unexpected failures
       0 unexpected stat failures
     479 fragile tests

However apparently it's failing on CI again. Now sure how/why it picks a happy locally but not on CI.

Build.hs Outdated
-- | GHC builder
--
-- Importantly, it doesn't link with the cabal library but use cabal-install
-- program instead (compared to e.g. Hadrian).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From a packaging perspective, this just shifts the problem. Just like hadrian, cabal-install's dependency closure is not negligible and it tends to be picky about dependency versions (sometimes even unnecessarily so). Bootstrapping GHC with Hadrian is bad since it involves a nontrivial amount of work getting its dependencies compiled. With this solution, the problem would just be shifted to bootstrapping cabal-install.

Cabal the library has the advantage that it is necessarily available since it's bundled (for better or worse) with your bootstrap GHC which you need anyways. As a consequence, I'd suggest

  • Either writing a small wrapper around Cabal which implements the functionality necessary for this build system (which is probably preferrable to outright linking against it since that would make it harder to switch to a Makefile later).
  • Use runghc Setup.hs instead of cabal-install. It looks like this would not work with the current code since you rely on e.g. project files which aren't supported. Also it carries some risk since Setup.hs isn't treated with the same amount of care as cabal-install by the upstream maintainers (so bigger refactors carry the risk of regressing Setup.hs which isn't always caught before a release).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sternenseemann I would simply do haskell.nix or cabal2nkx on it, vender the generated nix files, and cut out cabal-install that way.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sternenseemann this is actually a good point hmm, dunno why it hadn't rung any alarm for me earlier.

Well I am less worried about building cabal-install itself, though okay there is that too (I don't really think the deps are significantly worse than hadrian, and the big plus is it is decoupled from ghc unlike hadrian).
My concern now is rather that cabal-install typically needs a network connection at runtime.
But maybe it is possible to run it or make it run off-line too (Fedora does that for rust cargo)??
Otherwise distros would actually be in a worse position than with hadrian, since they need Cabal not cabal-install, so I agree with you there.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My concern now is rather that cabal-install typically needs a network connection at runtime.

This is effectively disabled by the setting active-repositories: :none in the generated cabal.project file which means that cabal would only use the pre existing package db. You can also pass --offline, but in my experience cabal frequently ignores this flag.

Well I am less worried about building cabal-install itself, though okay there is that too (I don't really think the deps are significantly worse than hadrian, and the big plus is it is decoupled from ghc unlike hadrian).

I suppose this hurts nixpkgs more than others. We always need to fully bootstrap anything, so we can never rely on a preexisting cabal-install that was built by our previous GHC package. (I'm not sure if Fedora does this, but I do know that Alpine heavily relies on this, also for the stage 0 GHC.) As a result, we need to build everything needed for bootstrapping GHC using the upstream bindists (or whatever bootstrap compiler we use). This can be finnicky and unreliable, so it is preferrable if less packages have to be built. Also, we try to use the GHC core packages as much as possible (like Stackage) which makes building cabal-install a nightmare because it has become really picky about filepath and directory as of late. Having support for a wide range of core package versions no longer seems to be a concern upstream (in fact, I've given up getting cabal to work for all GHC versions.)

I do agree, though, that cabal-install is not as bad as Hadrian. It's dependency closure is probably similar, but we would not need to compile cabal-install with stage0, specifically.

I would simply do haskell.nix or cabal2nkx on it, vender the generated nix files, and cut out cabal-install that way.

Well, we use Setup.hs for “cutting out cabal-install“ which is, of course, troublesome when it comes to stuff like project files which it doesn't support. I'll have to read through Build.hs and figure out whether cabal-install is used to manage the package-db for compiling and what the project file is used for, I guess.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sternenseemann they way I see it, Build.hs is pure tech debt, and intentionally so :) The problem with Hadrian is that it makes it "too easy and nice" to write custom logic for building GHC, whereas here the goal is to make sure GHC is "just" a bunch of cabal packages, and anything beyond that is awkard and sticks out like a sore thumb.

cabal and cabal-install are huge code bases, but I hope the relevant declarative parts of the cabal files here are much simpler and amendable to more implementation strategies --- i.e. it would be also bad if this ended up using a huge long tail of random cabal-install features, but I don't think that will be the case.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CC @andreabedini who is always talking about how "we shouldn't need bespoke Haskell tools to build Haskell projects".

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been tasked with building a GHC bindist with cabal-install instead of Hadrian but I don't have more insights on what is planned for distributions like Fedora or Nixpkgs after that. At least we would be relying on a properly released tool, which Hadrian isn't, so it should be a step in the right direction. It is definitely a step in the direction of reinstallable boot libraries to make the compiler multi-target, but that's another story.

Project files are used to pass options to build packages (cabal flags, CPP defines...) and to manage dependencies automatically. For now we only download happy and alex but we might as well vendor them in the future.

Copy link

@angerman angerman Feb 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GHC is not fully bootstrappable, we need to start with some binary blob anyway. As such I am not at all concerned about bootstrapping with a binary blob of cabal-install either. And indeed, the bindists we will build will contain a cabal-install executable. There will be static ones. A long term plan may be to use cabal, and derive a ninja, or nix solution alongside, as part of further cabal development. You'd still need some ghc bootstrap blob though.

Hadrian is a failure in so many respects. The biggest for me is that the haskell project, namely its compiler, effectively states, that cabal is not good enough to build it. And as such we spend countless hours and resources on a haphazardly build "alternative", instead of collectively improving cabal for everyone in the community. It's such a waste of resources and NIH syndrome. Another is that Hadrian follows closely the insanity we had with the Make base build system. GHC itself is not properly isolated, there is lots of reaching around packages, instead of properly packaging stuff. @Ericson2314 has addresses one of the long standing global configure issues. There are more similar issues where GHC is effectively treated as one massive amalgamated codebase, instead of a tree of dependencies.

Ultimately the whole symbiotic development of GHC and its build tool should be broken by separating both clearly, on top of improving cabal, which dearly needs a lot of improvements. These improvements should then help everyone on the community, instead of being features only found in Hadrian.

Hadrian must die, or at best be relegated to a tool for GHC developers only; it has served its purpose as a spring board from the old Make base build system to a more cabal'ified version. The logical conclusion is to build GHC with cabal, and improve cabal to the point where this is a no-brainer.

Build.hs is a temporary crutch, ultimately this should be cabal-install, as well as short Makefile, and maybe some tiny shell scripts to glue things together where patching/changing cabal-install is not sensible.

And lastly, just to make sure this is not misunderstood, all changes to GHC and cabal we make, we intent to upstream. This is not supposed to be a fork!

@hsyl20
Copy link
Author

hsyl20 commented Feb 11, 2025

The testsuite now runs on CI! See e.g. https://github.com/stable-haskell/ghc/actions/runs/13258505252/job/37009633702

Let's try to reduce the number of failures now:

SUMMARY for test run started at Tue Feb 11 08:54:59 2025 
0:48:53.161255 spent to go through
   10414 total tests, which gave rise to
   31954 test cases, of which
   21480 were skipped
       8 had missing libraries

    9530 expected passes
     118 expected failures

      51 caused framework failures
       0 caused framework warnings
       2 unexpected passes
     294 unexpected failures
       0 unexpected stat failures
     479 fragile tests

@hsyl20
Copy link
Author

hsyl20 commented Feb 12, 2025

Progress:

SUMMARY for test run started at Wed Feb 12 10:05:13 2025 
0:56:47.117837 spent to go through
   10414 total tests, which gave rise to
   31954 test cases, of which
   21480 were skipped
       8 had missing libraries
    9758 expected passes
     118 expected failures
       0 caused framework failures
       0 caused framework warnings
       2 unexpected passes
     117 unexpected failures
       0 unexpected stat failures
     479 fragile tests

@angerman
Copy link

Build system-cxx-std-lib package

Off, yea, that one needs to die 🤣 but not just yet.

@angerman
Copy link

@hsyl20
Copy link
Author

hsyl20 commented Feb 13, 2025

Build system-cxx-std-lib package

Off, yea, that one needs to die 🤣 but not just yet.

I've opened #10 to track this

hsyl20 added 10 commits April 15, 2025 15:27
We need to use wrappers, which is not pretty.

We can use the resulting target with -Bpath/to/targets/aarch64_linux/lib
but we can't link a program yet because we lack libffi for aarch64...
In the past we built libffi with rts, we might do it again?
This reverts commit 420310e.
This reverts commit 1ec1198.
@angerman angerman self-assigned this May 2, 2025
hsyl20 and others added 16 commits May 6, 2025 09:59
Use --target=foo to select a target installed in $topdir/targets/foo/
We will use nix and the devx action for now.  This is IOGs standard
tooling.  While we are not opposed to other options, this is for now
the de-facto way how we build stuff at IOG by default.
nix shell nixpkgs/nixos-unstable#zig nixpkgs/nixos-unstable#pkgsCross.aarch64-multiplatform.buildPackages.gcc

this should now get fairly far.
@angerman angerman changed the base branch from tmp/make-build-offset to wip/make-build-pre-zig May 27, 2025 00:08
@angerman
Copy link

#34 now contains the latest state of this.

@angerman angerman closed this May 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants